package com.walter.common.dataPermission.aspect;

import com.walter.common.dataPermission.DataPermissionFun;
import com.walter.common.dataPermission.annotation.DataPermission;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

/**
 * 操作数据权限校验 aop
 */
@Slf4j
@Aspect
@Component
public class DataScopeAspect {

    @Resource
    private Map<String, DataPermissionFun> funcNameBeanMap;

    private final StandardReflectionParameterNameDiscoverer discoverer = new StandardReflectionParameterNameDiscoverer();
    private final ExpressionParser parser = new SpelExpressionParser();

    /**
     * 织入数据校验的前置逻辑
     *
     * @param joinPoint
     */
    @Before("@annotation(dataPermission)")
    public void check(JoinPoint joinPoint, final DataPermission dataPermission) {
        if(!dataPermission.check()){
            return;
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        final Method method = signature.getMethod();
        //校验函数el表达式
        final String checkFunNames = dataPermission.checkFunctions();

        //绑定参数
        EvaluationContext evaluationContext = bindParam(method, joinPoint.getArgs());
        final Expression expression = parser.parseExpression(checkFunNames);
        boolean hasDataPermission = false;
        try {
            hasDataPermission = expression.getValue(evaluationContext, Boolean.class);
        } catch (Exception e) {
            log.error("方法:{} 参数:{} 校验数据权限失败", method.getDeclaringClass().getName() + "#" + method.getName(),
                    Arrays.toString(joinPoint.getArgs()), e);
        }

        log.debug("方法:{} 参数:{} 校验数据权限:{}", method.getDeclaringClass().getName() + "#" + method.getName(),
                Arrays.toString(joinPoint.getArgs()), hasDataPermission);

        if (!hasDataPermission) {
//            OpenApiCode.NOT_DATA_PERMISSION.throwException();
        }
    }

    /**
     * 将方法的参数名和参数值绑定
     *
     * @param method 方法，根据方法获取参数名
     * @param args   方法的参数值
     * @return
     */
    private EvaluationContext bindParam(Method method, Object[] args) {
        //获取方法的参数名
        String[] params = discoverer.getParameterNames(method);

        //将参数名与参数值对应起来
        EvaluationContext context = new StandardEvaluationContext();

        //将数据权限校验的方法放入计算的上下文中
        funcNameBeanMap.forEach((k, v) -> context.setVariable(k, v));

        if (Objects.isNull(params) || params.length == 0) {
            return context;
        }

        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], args[len]);
        }

        return context;
    }
}
